package tournamentmanager;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public abstract class AbstractTournament implements Tournament {
  private int sleepTime = 3000;
  private final List<TournamentChessEngine> playersList = new ArrayList<TournamentChessEngine>();
  private String outPGNFile = "tournament.pgn";
  private int timeControl = 5;
  private int movesPerSession = 40;
  private int timeIncrement = -1;
  private int gamesPlayed = 0;
  transient private MatchRunner matchRunner;
  transient private Set<TournamentObserver> observers;

  public void addPlayer(ChessEngine engine) {
    TournamentChessEngine tournamentPlayer = new TournamentChessEngine(engine);
    playersList.add(tournamentPlayer);
    tournamentPlayer.setPairingNumber(playersList.size());
  }

  public void removePlayer(int pairingNumber) {
    playersList.remove(pairingNumber - 1);
    for (int i = 0; i < playersList.size(); i++) {      
      playersList.get(i).setPairingNumber(i + 1);
    }
  }

  void runMatch() {
    try {
      matchRunner.run();
      gamesPlayed++;
      notifyObservers();
      sleepAfterGame();
    }
    catch (RuntimeException e) {
      throw new RuntimeException(e.getMessage() + "\n\nTournament interrupted!");
    }
    catch (InterruptedException e) {
      throw new RuntimeException("Tournament interrupted!");
    }
  }

  private void sleepAfterGame() throws InterruptedException {
    Thread.sleep(sleepTime);
  }

  public int getTimeControl() {
    return timeControl;
  }

  public int getMovesPerSession() {
    return movesPerSession;
  }

  public int getTimeIncrement() {
    return timeIncrement;
  }

  public int getGamesPlayed() {
    return gamesPlayed;
  }

  public String getOutPGNFile() {
    return outPGNFile;
  }

  public List<TournamentChessEngine> getPlayersList() {
    return playersList;
  }

  public void setOutPGNFile(String outPGNFile) {
    if ("".equals(outPGNFile.trim())) {
      this.outPGNFile = "tournament.pgn";
      return;
    }
    this.outPGNFile = outPGNFile;
  }

  public void setTimeControl(int timeControl) {
    this.timeControl = timeControl;
  }

  public void setMovesPerSession(int movesPerSession) {
    this.movesPerSession = movesPerSession;
  }

  public void setTimeIncrement(int timeIncrement) {
    this.timeIncrement = timeIncrement;
  }

  public void setGamesPlayed(int gamesPlayed) {
    if (gamesPlayed < 0) {
      this.gamesPlayed = 0;
    } else {
      this.gamesPlayed = gamesPlayed;
    }
  }

  public void setMatchRunner(MatchRunner matchRunner) {
    this.matchRunner = matchRunner;
  }

  public MatchRunner getMatchRunner() {
    return matchRunner;
  }

  public void setSleepTime(int sleepTime) {
    this.sleepTime = sleepTime;
  }

  public void registerObserver(TournamentObserver observer) {
    getObservers().add(observer);
  }

  public void removeObserver(TournamentObserver observer) {
    observers.remove(observer);
  }

  private void notifyObservers() {
    for (TournamentObserver observer : getObservers()) {
      observer.update(this);
    }
  }

  private Set<TournamentObserver> getObservers() {
    if (observers == null) {
      observers = new HashSet<TournamentObserver>();
    }
    return observers;
  }

  public boolean isEnded() {
    return gamesPlayed >= getGamesToPlay();
  }

  public boolean isInProgress() {
    return gamesPlayed > 0 && gamesPlayed < getGamesToPlay();
  }

}
